package com.robertux.leaderboard.servlets;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.robertux.leaderboard.beans.EstadoEntidad;
import com.robertux.leaderboard.beans.JsonResponse;
import com.robertux.leaderboard.beans.ModalidadGrupoProducto;
import com.robertux.leaderboard.beans.RolUsuario;
import com.robertux.leaderboard.beans.entities.DetalleGrupoProducto;
import com.robertux.leaderboard.beans.entities.GrupoProducto;
import com.robertux.leaderboard.beans.entities.external.Producto;
import com.robertux.leaderboard.util.MethodExecutionSecurity;
import com.robertux.leaderboard.util.MethodParamsChecker;
import com.robertux.leaderboard.util.gestoresModelos.GestorGruposProductos;

@WebServlet("/GruposProductosServlet/*")
public class GruposProductosServlet extends BaseServlet {
	private static final long serialVersionUID = 1L;
	private Logger logger;
	private GestorGruposProductos gestorGps;
       
    /**
     * @see BaseServlet#BaseServlet()
     */
    public GruposProductosServlet() {
        super();
        this.logger = Logger.getLogger(this.getClass());
        this.gestorGps = new GestorGruposProductos();
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"page", "rowsPerPage", "filter"})
    public JsonResponse getGruposProductos(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonArray arr = new JsonArray();
    	long totalGruposProductos = 0;
    	
    	if(req.getParameter("refresh") != null) {
    		this.gestorGps = new GestorGruposProductos();
    	}
    	
    	totalGruposProductos = this.gestorGps.getTotalGruposProductos(req.getParameter("filter"));
    	
    	for(GrupoProducto grp: this.gestorGps.getGruposProductos(Integer.parseInt(req.getParameter("page")), Integer.parseInt(req.getParameter("rowsPerPage")), req.getParameter("filter"))) {
    		JsonObject grpObj = new JsonObject();
    		ModalidadGrupoProducto modalidad = ModalidadGrupoProducto.fromCodigo(grp.getModalidad());
    		grpObj.addProperty("gPrdId", grp.getId());
    		grpObj.addProperty("name", grp.getNombre());
    		grpObj.addProperty("prdQty", grp.getDetalleGrupoProductos().size());
    		grpObj.addProperty("modalidad", modalidad.getCodigo());
    		grpObj.addProperty("nombreModalidad", modalidad.getDescripcion());
    		grpObj.addProperty("selected", false);
    		arr.add(grpObj);
    	}
    	
    	JsonResponse jsonResp = new JsonResponse(0, "OK", "productGroups", arr);
    	jsonResp.add("total", new JsonPrimitive(totalGruposProductos));
    	return jsonResp;
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    public JsonResponse getTodosGruposProductos(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonArray arr = new JsonArray();
    	
    	for(GrupoProducto grp: this.gestorGps.getGruposProductos()) {
    		JsonObject grpObj = new JsonObject();
    		ModalidadGrupoProducto modalidad = ModalidadGrupoProducto.fromCodigo(grp.getModalidad());
    		grpObj.addProperty("gPrdId", grp.getId());
    		grpObj.addProperty("name", grp.getNombre());
    		grpObj.addProperty("prdQty", grp.getDetalleGrupoProductos().size());
    		grpObj.addProperty("modalidad", modalidad.getCodigo());
    		grpObj.addProperty("nombreModalidad", modalidad.getDescripcion());
    		grpObj.addProperty("selected", false);
    		arr.add(grpObj);
    	}
    	
    	JsonResponse jsonResp = new JsonResponse(0, "OK", "productGroups", arr);
    	return jsonResp;
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"gprdId"})
    public JsonResponse getDetalleGrupoProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonResponse jsonResp = new JsonResponse();

    	GrupoProducto gPrd = null;
    	if((gPrd = this.gestorGps.getGrupoProducto(Integer.parseInt(req.getParameter("gprdId")))) != null) {
	    	jsonResp.add("gprdId", new JsonPrimitive(gPrd.getId()));
	    	jsonResp.add("name", new JsonPrimitive(gPrd.getNombre()));
	    	jsonResp.add("modalidad", new JsonPrimitive(gPrd.getModalidad()));
	    	jsonResp.add("productQty", new JsonPrimitive(gPrd.getDetalleGrupoProductos().size()));
	    	
	    	Map<String, Producto> mapaProductos = this.getProductosSesion(req.getSession(true));
	    	for(Producto prd: mapaProductos.values()) {
	    		prd.setCantidad(0);
	    		prd.setSeleccionado(false);
	    	}
	    	
	    	for(DetalleGrupoProducto dGPrd: gPrd.getDetalleGrupoProductos()) {
	    		if(mapaProductos.containsKey(dGPrd.getProductoId())) {
	    			mapaProductos.get(dGPrd.getProductoId()).setSeleccionado(true);
	    			mapaProductos.get(dGPrd.getProductoId()).setCantidad(dGPrd.getCantidad());
	    		}
	    	}
    	} else {
    		return JsonResponse.fromError(2028, req.getParameter("gPrdId"));
    	}
    	
    	return jsonResp;
    }
    
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"page", "rowsPerPage", "filter"})
    public JsonResponse getProductos(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonArray arr = new JsonArray();
    	int page = Integer.parseInt(req.getParameter("page"));
    	int rowsPerPage = Integer.parseInt(req.getParameter("rowsPerPage"));
    	String filter = req.getParameter("filter");
    	int totalFiltrados = 0;
    	
    	List<Producto> productos = new ArrayList<Producto>(this.getProductosSesion(req.getSession(true)).values());
    	Collections.sort(productos, new Comparator<Producto>() {
			@Override
			public int compare(Producto o1, Producto o2) {
				int compareBySelected = new Boolean(o1.isSeleccionado()).compareTo(new Boolean(o2.isSeleccionado()));
				if(compareBySelected != 0) {
					return (compareBySelected * -1);
				} else {
					return o1.getNombre().compareTo(o2.getNombre()); //Coompare by name
				}
			}
		});
    	List<Producto> filteredProductos = this.filtrarProductos(productos, filter);
    	totalFiltrados = filteredProductos.size();
    	
    	
    	for(Producto prd: this.paginarProductos(filteredProductos, page, rowsPerPage)) {
    		JsonObject prdObj = new JsonObject();
			prdObj.addProperty("id", prd.getId());
			prdObj.addProperty("name", prd.getNombre());
			prdObj.addProperty("qty", prd.getCantidad());
			prdObj.addProperty("selected", prd.isSeleccionado());
			arr.add(prdObj);
    	}
    	
    	JsonResponse jsonResp = new JsonResponse(0, "OK", "products", arr);
    	jsonResp.add("total", new JsonPrimitive(totalFiltrados));
    	return jsonResp;
    }
    
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"prdId", "select", "filter"})
    public JsonResponse selectProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	
    	if(req.getParameter("prdId").equals("ALL")) {
    		List<Producto> productosFiltrados = this.filtrarProductos(this.getProductosSesion(req.getSession(true)).values(), req.getParameter("filter"));
    		for(Producto prd: productosFiltrados) {
	    		prd.setSeleccionado(req.getParameter("select").equals("YES"));
	    		if(prd.getCantidad() == 0) {
	    			prd.setCantidad(1);
	    		}
	    		this.getProductosSesion(req.getSession(true)).get(prd.getId()).setSeleccionado(req.getParameter("select").equals("YES"));
    		}
    		this.logger.debug("Actualizados " + productosFiltrados.size() + ". Seleccionados: " + req.getParameter("select").equals("YES"));
    	} else {
	    	Producto prd = this.getProductosSesion(req.getSession(true)).get(req.getParameter("prdId"));
	    	if(prd != null) {
	    		prd.setSeleccionado(req.getParameter("select").equals("YES"));
	    		if(prd.isSeleccionado() && prd.getCantidad() == 0) {
	    			prd.setCantidad(1);
	    		}
	    	}
    	}
    	
    	return new JsonResponse();
    }
    
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"prdId", "qty"})
    public JsonResponse updateQtyProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	Map<String, Producto> productos = this.getProductosSesion(req.getSession(true));
    	
    	Producto prd = productos.get(req.getParameter("prdId"));
    	if(prd != null) {
    		prd.setCantidad(Integer.parseInt(req.getParameter("qty")));
    	}
    	return new JsonResponse();
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"nombre", "modalidad"})
    public JsonResponse addGrupoProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	GrupoProducto gPrdExistente = null;
    	if((gPrdExistente = this.gestorGps.getGrupoProducto(req.getParameter("nombre"))) != null
    			&& gPrdExistente.getEstado().equals(EstadoEntidad.ACTIVO.getCodigo())) {
    		return JsonResponse.fromError(1031);
    	}
    	
    	GrupoProducto gPrd = new GrupoProducto(req.getParameter("nombre"), EstadoEntidad.ACTIVO.getCodigo(), Integer.parseInt(req.getParameter("modalidad")));
    	for(Producto prd: this.getProductosSesion(req.getSession(true)).values()) {
    		if(prd.isSeleccionado()) {
    			gPrd.addDetalleGrupoProducto(new DetalleGrupoProducto(prd.getId(), prd.getCantidad()));
    		}
		}
    	if(gPrd.getDetalleGrupoProductos().size() == 0) {
    		return JsonResponse.fromError(1035);
    	}
    	
    	try {
			if(this.gestorGps.agregarGrupoProducto(gPrd)) {
				return new JsonResponse(0, "Grupo de productos agregado satisfactoriamente");
			} else {
				return JsonResponse.fromError(1032);
			}
		} catch (Exception e) {
			return JsonResponse.fromError(1032);
		}
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"gPrdId", "nombre", "modalidad"})
    public JsonResponse updateGrupoProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	GrupoProducto gPrd = null, gPrdExistente = null;
    	
    	if((gPrd = this.gestorGps.getGrupoProducto(Integer.parseInt(req.getParameter("gPrdId")))) != null) {
    		if((gPrdExistente = this.gestorGps.getGrupoProducto(req.getParameter("nombre"))) != null 
    				&& gPrdExistente.getId() != gPrd.getId() 
    				&& gPrdExistente.getEstado().equals(EstadoEntidad.ACTIVO.getCodigo())) {
    			return JsonResponse.fromError(1031);
    		}
    		
    		gPrd.setNombre(req.getParameter("nombre"));
    		gPrd.setModalidad(Integer.parseInt(req.getParameter("modalidad")));
    		try {
    			for(DetalleGrupoProducto detGPrd: gPrd.getDetalleGrupoProductos()) {
    				this.gestorGps.eliminarDetalleProducto(detGPrd);
    			}
				gPrd.getDetalleGrupoProductos().clear();
				this.gestorGps.actualizarGrupoProducto(gPrd);
				for(Producto prd: this.getProductosSesion(req.getSession(true)).values()) {
		    		if(prd.isSeleccionado()) {
		    			gPrd.addDetalleGrupoProducto(new DetalleGrupoProducto(prd.getId(), prd.getCantidad()));
		    		}
				}
				
				if(gPrd.getDetalleGrupoProductos().size() == 0) {
		    		return JsonResponse.fromError(1035);
		    	}
				
				this.gestorGps.actualizarGrupoProducto(gPrd);
				return new JsonResponse(0, "Grupo de productos modificado satisfactoriamente");
				
			} catch (Exception e) {
				this.logger.error("Error tratando de actualizar el grupo de productos: " + e.getMessage(), e);
				return JsonResponse.fromError(1030);
			}
    	} else {
    		return JsonResponse.fromError(1028, req.getParameter("gPrdId"));
    	}
    }

    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"gPrdId"})
    public JsonResponse delGrupoProducto(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	GrupoProducto gPrd = null;
    	if((gPrd = this.gestorGps.getGrupoProducto(Integer.parseInt(req.getParameter("gPrdId")))) != null) {
    		if(this.gestorGps.eliminarGrupoProducto(gPrd)) {
    			return new JsonResponse(0, "Grupo de productos eliminado satisfactoriamente");
    		} else {
    			return JsonResponse.fromError(2029);
    		}
    	} else {
    		return JsonResponse.fromError(2028, req.getParameter("gPrdId"));
    	}
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    @MethodParamsChecker(requiredParams={"gPrdId"})
    public JsonResponse setCurrentGprdId(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	this.logger.debug("Guardando grupo de productos " + req.getParameter("gPrdId"));
    	req.getSession().setAttribute("currentGPrdId", req.getParameter("gPrdId"));
    	return new JsonResponse();
    }
    
    @MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.SUPERVISOR)
    public JsonResponse getModalidadesGruposProductos(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonResponse jsonResp = new JsonResponse();
    	JsonArray arr = new JsonArray();
    	for(ModalidadGrupoProducto modalidad: ModalidadGrupoProducto.values()) {
    		JsonObject obj = new JsonObject();
    		obj.addProperty("codigo", modalidad.getCodigo());
    		obj.addProperty("descripcion", modalidad.getDescripcion());
    		arr.add(obj);
    	}
    	
    	jsonResp.add("modalidades", arr);
    	return jsonResp;
    }
    
    @SuppressWarnings("unchecked")
	protected Map<String, Producto> getProductosSesion(HttpSession session) {
		if(session.getAttribute("productos") == null) {
    		session.setAttribute("productos", this.gestorGps.getProductosMap());
    	}
    	return (Map<String, Producto>)session.getAttribute("productos");
	}
    
    protected List<Producto> filtrarProductos(Collection<Producto> productos, String filter) {
    	List<Producto> filteredProductos;
    	
    	if(filter.trim().length() > 0) {
    		filteredProductos = new ArrayList<Producto>();
	    	for(Producto prd: productos) {
	    		if(prd.getNombre().toUpperCase().contains(filter.toUpperCase())) {
	    			Producto filterPrd = new Producto(prd.getId(), prd.getNombre(), prd.getCantidad(), prd.getPrecio());
	    			filterPrd.setSeleccionado(prd.isSeleccionado());
	    			filteredProductos.add(filterPrd);
	    		}
	    	}
    	} else {
    		filteredProductos = new ArrayList<Producto>(productos);
    	}
    	
    	return filteredProductos;
    }
    
	protected List<Producto> paginarProductos(List<Producto> productos, int page, int rowsPerPage) {
    	List<Producto> pagedProductos = new ArrayList<Producto>();
    	
    	int start = ((page-1) * rowsPerPage);
    	int end = (page * rowsPerPage)-1;
    	this.logger.debug("start: " + start + ". end: " + end + ". total: " + productos.size());
    	
    	for(int i=start; i<=end; i++) {
	    	try {
	    		Producto prd = new Producto(productos.get(i).getId(), productos.get(i).getNombre(), productos.get(i).getCantidad(), productos.get(i).getPrecio());
	    		prd.setSeleccionado(productos.get(i).isSeleccionado());
	    		pagedProductos.add(prd);
			} catch (Exception e) {
				this.logger.error("Error buscando el producto en la lista con el �ndice " + i + ": " + e.getMessage(), e);
			}
    	}
    	return pagedProductos;
    }
}
